/*
 * Lenovo Group
 * Copyright (c) 2015-2016 All Rights Reserved.
 * 
 * Project Name:assign-tracer
 * Create Time: 2016年2月14日 上午10:34:31
 */
package com.lenovo.lmrp.server.assigntracer.domain.ticket;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.lenovo.lmrp.api.constant.MessageTemplateType;
import com.lenovo.lmrp.api.constant.TicketPolicy;
import com.lenovo.lmrp.api.constant.TicketStatus;
import com.lenovo.lmrp.api.itsmgateway.IItsmTicket;
import com.lenovo.lmrp.api.model.entity.EngineerPO;
import com.lenovo.lmrp.api.model.entity.MessageTemplatePO;
import com.lenovo.lmrp.api.model.entity.ProcessPO;
import com.lenovo.lmrp.api.model.entity.ProjectEngineerPO;
import com.lenovo.lmrp.api.model.entity.TicketEventPO;
import com.lenovo.lmrp.api.model.entity.TicketPO;
import com.lenovo.lmrp.api.model.entity.TicketProcessPO;
import com.lenovo.lmrp.api.model.entity.TicketProcessPO.ProcessInstStatus;
import com.lenovo.lmrp.api.model.view.ChangedTicketVO;
import com.lenovo.lmrp.api.model.view.EngineerVO;
import com.lenovo.lmrp.base.event.EventCode;
import com.lenovo.lmrp.base.event.EventContent;
import com.lenovo.lmrp.base.event.EventSource;
import com.lenovo.lmrp.base.event.EventTopic;
import com.lenovo.lmrp.base.mq.MQTopic;
import com.lenovo.lmrp.server.assigntracer.RestClientFactory;
import com.lenovo.lmrp.server.assigntracer.domain.service.QuerySelector;
import com.lenovo.lmrp.server.assigntracer.domain.service.TicketService;
import com.lenovo.lmrp.server.assigntracer.domain.workflow.IProcessService;
import com.lenovo.lmrp.server.assigntracer.domain.workflow.ProcessSelector;
import com.lenovo.lmrp.server.assigntracer.facade.TicketNotificationFacadeImpl;
import com.lenovo.lmrp.server.assigntracer.repository.ARepositoryFactory;
import com.lenovo.lmrp.server.assigntracer.util.cache.AssignTracerCache;
import com.lenovo.lmrp.server.assigntracer.util.grap.GrabFactory;
import com.lenovo.lmrp.server.assigntracer.util.grap.IGrab;
import com.lenovo.lmrp.server.assigntracer.util.pusher.PushParameter;
import com.lenovo.lmrp.server.assigntracer.util.pusher.SenderUtil;
import com.lenovo.lmrp.server.assigntracer.util.pusher.TransformUtil;
import com.lenovo.sdc.framework.cache.ACacheClient;
import com.lenovo.sdc.framework.cache.CacheItem;
import com.lenovo.sdc.framework.dao.ADaoFactory;
import com.lenovo.sdc.framework.dao.ICommonDAO;
import com.lenovo.sdc.framework.mq.AMQClient;
import com.lenovo.sdc.framework.rest.RestClientProxy;
import com.lenovo.sdc.framework.util.CommonUtil;
import com.lenovo.sdc.framework.util.exception.ErrorCode;
import com.lenovo.sdc.framework.util.exception.TraceableException;

/**
 * @author songyz
 *
 */
public class TicketBO {
	private String itsmCode;
	private TicketPO po;
	private TicketService ticketService;
	private AMQClient mqclient;

	private static Logger logger = LoggerFactory.getLogger(TicketNotificationFacadeImpl.class);

	public TicketBO(String itsmCode, TicketPO po, TicketService ticketService) {
		if (Objects.isNull(po)) {
			throw new TraceableException(ErrorCode.COMM_PARAM_MISSED,
					"TicketBO create faild, because the ticketId does not exist.");
		}
		this.itsmCode = itsmCode;
		this.po = po;
		this.ticketService = ticketService;
	}

	public TicketBO(String itsmCode, TicketPO po, TicketService ticketService, AMQClient mqclient) {
		if (Objects.isNull(po)) {
			throw new TraceableException(ErrorCode.COMM_PARAM_MISSED,
					"TicketBO create faild, because the ticketId does not exist.");
		}
		this.itsmCode = itsmCode;
		this.po = po;
		this.ticketService = ticketService;
		this.mqclient = mqclient;
	}

	/**
	 * choice a suitable workflow for ticket and create process instance.
	 * 
	 * @param itsmcode
	 * @param ticketId
	 * @throws if
	 *             not found
	 */
	public void startTicketProcess(ProcessSelector selector, IProcessService proccessService) {
		try {
			// choice a process definition base on po propterties
			ProcessPO ppo = selector.choiceProcess(itsmCode, po);
			TicketProcessPO tpp = null;
			try {
				tpp = ARepositoryFactory.getFactory().getWorkflowRepository().createTicketProcessPO(ppo.getProcessId(),
						itsmCode, po.getId());
			} catch (TraceableException ex) {
				// if ticket hasn't process instance, then remove the process
				// instance, create a new process instance
				if (ex.getErrorCode().equals(ErrorCode.COMM_PARAM_INVALID)) {
					TicketProcessPO oldtpp = ARepositoryFactory.getFactory().getWorkflowRepository()
							.getRuningTicketProcess(itsmCode, po.getId());
					proccessService.stopProcessInstance(oldtpp.getInstanceId());
					oldtpp.setStatus(ProcessInstStatus.STOPED);
					oldtpp.setEndTime(new Date());
					ARepositoryFactory.getFactory().getWorkflowRepository().saveTicketProcessPO(oldtpp);
					tpp = ARepositoryFactory.getFactory().getWorkflowRepository()
							.createTicketProcessPO(ppo.getProcessId(), itsmCode, po.getId());
				} else {
					throw ex;
				}
			}
			// start process instance
			Map<String, Object> params = new HashMap<>();

			params.put("itsmCode", itsmCode);
			params.put("ticketPO", po);

			// TODO, process configurable param will stored in db when deploy
			// process
			// currently, just put system.env for test
			params.put("RETRY_INTERVAL", "PT" + System.getProperty("RETRY_INTERVAL", "10") + "M");

			proccessService.startProcessInstance(tpp.getInstanceId(), params);
			logger.debug("process RETRY_INTERVAL {}", params.get("RETRY_INTERVAL"));
			// 将抢单策略的容器进行初始化，并存入缓存中，缓存时间设置为30分钟
			ACacheClient client = AssignTracerCache.getClient();
			IGrab iGrab = GrabFactory.getIGrab();
			client.put(new CacheItem<IGrab>(po.getId(), iGrab, itsmCode, GrabFactory.GRAB_CACHE_TIME));
		} catch (Exception e) {

		}
	}

	/**
	 * 给一组工程师发送消息
	 * 
	 * @param ticketPO
	 * @param groupto
	 * @param policy
	 * @param messageTemplateType
	 */
	public void sendMessageOnGroup(int groupto, MessageTemplateType messageTemplateType) {
		// query engineer base on project groupto value
		QuerySelector queryHelper = new QuerySelector();
		List<EngineerPO> engineerPOList = queryHelper.getEngineerPOsByGroupto(itsmCode, po, groupto);

		// 记录入库
		ticketService.storeAssigningTicketPO(itsmCode, engineerPOList, po);

		// 查询消息模板
		List<MessageTemplatePO> messageTemplateList = queryHelper.getMessageTemplateList(itsmCode, messageTemplateType);
		for (EngineerPO epo : engineerPOList) {
			send(epo, messageTemplateList);
		}
	}

	/**
	 * 向单个工程师推送派单消息
	 * 
	 * @param ticketPO
	 * @param engineerId
	 * @param policy
	 * @param messageTemplateType
	 */
	public void sendMessageOne() {
		if (logger.isDebugEnabled()) {
			logger.debug("派单提醒：engineerId:{},ticketId:{}", po.getAssignee(), po.getId());
		}

		EngineerPO engineerPO;
		try (ICommonDAO dao = ADaoFactory.getDefault().createDAO(itsmCode)) {
			engineerPO = dao.get(EngineerPO.class, po.getAssignee());
			if (Objects.isNull(engineerPO)) {
				throw new TraceableException(ErrorCode.DB_RECORD_NOTFOUND, "engineerid=" + po.getAssignee());
			}
		} catch (TraceableException exp) {
			throw exp;
		} catch (Exception exp) {
			throw new TraceableException(ErrorCode.DB_QUERY_FAILED, exp);
		}

		po.setPolicy(TicketPolicy.ACCEPT.getPolicy());// 此处修改肯会有问题，

		// 派单入库
		List<EngineerPO> engineerPOList = new ArrayList<>();
		engineerPOList.add(engineerPO);
		ticketService.storeAssigningTicketPO(itsmCode, engineerPOList, po);

		// 查询消息模板
		QuerySelector queryHelper = new QuerySelector();
		List<MessageTemplatePO> messageTemplateList = queryHelper.getMessageTemplateList(itsmCode,
				MessageTemplateType.DISPATCH);
		send(engineerPO, messageTemplateList);
	}

	/**
	 * 更新工单在无人抢单下的最终受理人， 调用ITSMGateWay 更新工单状态
	 */
	public void SendMessageToSiteManager() {
		// 查询无人抢单的最终受理人
		QuerySelector queryHelper = new QuerySelector();
		ProjectEngineerPO projectEngineer = queryHelper.getTicketFinalEngineer(itsmCode, po);
		if (Objects.isNull(projectEngineer)) {
			po.setStatus(TicketStatus.TOSITE.getStatus());
			po.setUpdateTime(new Date());
			ticketService.completeGrab(itsmCode, null, po);
			TraceableException.writeLog(ErrorCode.COMM_PARAM_INVALID, "抢单超时，通知场站或工程师不存在,ticketId:" + po.getId(), null);
			// logger.error("抢单超时，通知场站或工程师不存在. ticketId:{}", po.getId());
			// throw new TraceableException(ErrorCode.COMM_ERROR,
			// "抢单超时，通知场站或工程师不存在. ticketId:" + po.getId());
			return;
		}
		EngineerPO finialEngineer = projectEngineer.getEngineer();
		po.setAssignee(finialEngineer.getId());

		RestClientProxy itsmProxy = RestClientFactory.getItsmGatewayClient();
		IItsmTicket itsmTicket = itsmProxy.getService(IItsmTicket.class);
		ChangedTicketVO changedTicketVO = new ChangedTicketVO();
		Map<String, String> fields = new HashMap<>();

		if (projectEngineer.getSiteAdmin()) {
			if (logger.isDebugEnabled())
				logger.debug("场站管理员：the Ticket final engineerId:{}", projectEngineer.getEngineerId());

			po.getOthers().getAttributeBean().addAttribute("isManager", 1);
			// FIXME 此处代码写死，后期重构
			changedTicketVO.setAction("tosite");
			changedTicketVO.setReason("Change in ticket, notify the coordinator");
			// 规定的抢单时间内无人抢，将受理人改为场站管理员，并删除推送中间表记录
			po.setStatus(TicketStatus.TOSITE.getStatus());
			po.setUpdateTime(new Date());
		} else {
			if (logger.isDebugEnabled())
				logger.debug("抢单变成派单：the Ticket final engineerId:{}", projectEngineer.getEngineerId());
			// 假如是派单，将工单的派单人设置为默认的工程师
			po.getOthers().getAttributeBean().addAttribute("isManager", 0);
			po.setStatus(TicketStatus.ACCEPTING.getStatus());
			po.setUpdateTime(new Date());

			changedTicketVO.setAction("toEngineer");
			changedTicketVO.setReason("Change in ticket, notify the engineer");
			// 推送派单消息提醒工程师
			sendMessageOne();
		}

		fields.put("assigneeloginname", finialEngineer.getId());
		fields.put("assigneefullname", finialEngineer.getFullName());
		// 调用ITSM工单更新接口
		changedTicketVO.setFields(fields);
		itsmTicket.updateTicket(itsmCode, po.getId(), changedTicketVO.getAction(), changedTicketVO);

		ticketService.completeGrab(itsmCode, finialEngineer.getId(), po);
	}

	/**
	 * 工单变更提醒
	 * 
	 * @param itsmcode
	 * @param ticketId
	 */
	public void notifyClient(String ticketId) {
		EngineerPO engineerPO = null;
		try (ICommonDAO dao = ADaoFactory.getDefault().createDAO(itsmCode)) {
			engineerPO = dao.get(EngineerPO.class, po.getAssignee());
		} catch (Exception e) {
			throw new TraceableException(ErrorCode.DB_QUERY_FAILED, e);
		}
		//备注变更通知客户端
		if (po.getOthers().getAttributeBean().getAttribute("changenottobeview", 0) == 0) {
			logger.debug("ticket update don't notify app except surveynote chang.");
			return;
		}
		// 查询消息模板
		QuerySelector queryHelper = new QuerySelector();
		List<MessageTemplatePO> messageTemplateList = queryHelper.getMessageTemplateList(itsmCode,
				MessageTemplateType.ITSM_REPAIRCHANGE_REMIND);
		EngineerVO engineerVO = TransformUtil.tranEngineerPO2VO(engineerPO);
		engineerVO.setItsmcode(itsmCode);

		// 步骤四，查询所有设备
		engineerVO.setEngineerOnLineCids(queryHelper.getEngineerOnLineCids(itsmCode, po.getAssignee()));
		engineerVO.setContent(po);
		SenderUtil.sendMessageCreateCache(engineerVO, messageTemplateList);
		logger.debug("ticket surveynote changed send message to app.");
	}

	/**
	 * 抢单完成
	 * 
	 * @param engineerID
	 */
	public void completeGrab(String engineerID) {
		// 抢单完成，更新工单的受理人
		po.setAssignee(engineerID);
		ticketService.completeGrab(itsmCode, engineerID, po);
		TicketEventPO event = new TicketEventPO();
		event.setAction("confirm");
		event.setEventTime(new Date());
		event.setItsmCode(itsmCode);
		event.setSource(MQTopic.APPGWEVENT.name());
		event.setTicketID(po.getId());
		EventContent<TicketEventPO> content = new EventContent<>();
		content.setSource(EventSource.TRACER);
		content.setCode(EventCode.TICKET_UPDATE);
		content.setContent(event);
		mqclient.publish(EventTopic.TICKET_EVENT.name(), content.toJson());
	}

	private void send(EngineerPO engineerPO, List<MessageTemplatePO> messageTemplateList) {
		EngineerVO engineerVO = TransformUtil.tranEngineerPO2VO(engineerPO);
		engineerVO.setItsmcode(itsmCode);

		// 查询并设置工程师在线的cid
		QuerySelector queryHelper = new QuerySelector();
		engineerVO.setEngineerOnLineCids(queryHelper.getEngineerOnLineCids(itsmCode, engineerPO.getId()));
		engineerVO.setContent(po);
		SenderUtil.sendMessageCreateCache(engineerVO, messageTemplateList);
	}
}
